package editor; import java.awt.Component; import javax.swing.ComboBoxModel; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicComboBoxRenderer; @SuppressWarnings("unchecked") public abstract class KeySelectionRenderer extends BasicComboBoxRenderer implements JComboBox.KeySelectionManager { private static final long serialVersionUID = 2334229124492928364L; private long currentTime; private long lastTime; private final long timeFactor; private String prefix = ""; /** * This class can be used as the renderer and KeySelectionManager for an Object added to the ComboBoxModel. * * <p> * The class must be extended and the getDisplayValue() method must be implemented. This method will return a String to be rendered in the JComboBox. The same String will be used to do key selection of an item in the ComboBoxModel. * * @param box * A JComboBox object that is to use this KeySelectionRenderer. */ @SuppressWarnings("rawtypes") public KeySelectionRenderer(JComboBox box) { box.setRenderer(this); box.setKeySelectionManager(this); Long value = (Long) UIManager.get("ComboBox.timeFactor"); timeFactor = (value == null ? 250L : value.longValue()); } @SuppressWarnings("rawtypes") public KeySelectionRenderer(JList list) { list.setCellRenderer(this); Long value = (Long) UIManager.get("ComboBox.timeFactor"); timeFactor = (value == null ? 250L : value.longValue()); } /** * Implements the renderer. */ @Override @SuppressWarnings("rawtypes") public Component getListCellRendererComponent(JList list, Object item, int index, boolean isSelected, boolean hasFocus) { super.getListCellRendererComponent(list, item, index, isSelected, hasFocus); if (item != null) { this.setText(getDisplayValue(item)); } return this; } /** * Implements the KeySelectionManager. * */ @Override @SuppressWarnings("rawtypes") public int selectionForKey(char aKey, ComboBoxModel model) { this.currentTime = System.currentTimeMillis(); // Get the index of the currently selected item int size = model.getSize(); int startIndex = -1; Object selectedItem = model.getSelectedItem(); if (selectedItem != null) { for (int i = 0; i < size; i++) { if (selectedItem == model.getElementAt(i)) { startIndex = i; break; } } } // Determine the "prefix" to be used when searching the model. The // prefix can be a single letter or multiple letters depending on how // fast the user has been typing and on which letter has been typed. if (currentTime - lastTime < timeFactor) { if ((prefix.length() == 1) && (aKey == prefix.charAt(0))) // Subsequent same key presses move the keyboard focus to the next // object that starts with the same letter. startIndex++; else prefix += aKey; } else { startIndex++; prefix = "" + aKey; } lastTime = currentTime; // Search from the current selection and wrap when no match is found if (startIndex < 0 || startIndex >= size) startIndex = 0; int index = getNextMatch(prefix, startIndex, size, model); if (index < 0) // Wrap index = getNextMatch(prefix, 0, startIndex, model); return index; } /** * Finds the index of the item in the model that starts with the prefix. */ @SuppressWarnings("rawtypes") private int getNextMatch(String prefix, int start, int end, ComboBoxModel model) { for (int i = start; i < end; i++) { Object item = model.getElementAt(i); if (item != null) { String displayValue = getDisplayValue(item).toLowerCase(); if (displayValue.startsWith(prefix)) return i; } } return -1; } /** * This method must be implemented in the extended class. * * @param item * an item from the ComboBoxModel * @returns a String containing the text to be rendered for this item. */ public abstract String getDisplayValue(Object item); }